Laden von unkomprimierten und Run Length Encoded TGA Bildern von Evan "terminate" Pipho.
Ich habe viele Leute in #gamedev, den gamedev Foren und andere Orten, fragen sehen, wie man TGA Dateien lädt. Der folgende Code und die Erklärung werden Ihnen zeigen wie man sowohl unkomprimierte als auch RLE kompromierte Dateien lädt. Dieses Tutorial ist sehr auf OpenGL gemünzt, aber ich plane in Zukunft ein Tutorial zu schreiben welches genereller ist.
Wir fangen mit den zwei Header Dateien an. Die erste Datei wird unsere Textur-Struktur enthalten, die zweite, Strukturen und Variablen die vom Laden-Code benutzt werden.
Wie bei jeder Header Datei benötigen wir einen 'Inclusion Guards' um ein mehrfaches einbinden zu vermeiden.
Am Anfang der Datei fügen Sie diese Zeilen hinzu:
#ifndef __TEXTURE_H__ // Schaue, ob der Header bereits definiert wurde #define __TEXTURE_H__ // wenn nicht, definiere ihn.
#endif // __TEXTURE_H__ Ende vom Inclusion Guard
#pragma comment(lib, "OpenGL32.lib") // Linke Opengl32.lib #include < windows.h> // Standard Windows Header #include < stdio.h> // Standard Header für Datei I/O #include < gl\gl.h> // Standard Header für OpenGL
typedef struct
{
GLubyte* imageData; // enthält all die Farbwerte für das Bild.
GLuint bpp; // Enthält die Anzahl an Bits pro Pixel.
GLuint width; // Die Breite des gesamten Bildes.
GLuint height; // Die Höhe des gesamten Bildes.
GLuint texID; // Textur ID zur Verwendung mit glBindTexture.
GLuint type; // wie die Daten in * ImageData gespeichert sind (GL_RGB oder GL_RGBA)
} Texture;
typedef struct
{
GLubyte Header[12]; // Datei Header um die Datei Art zu bestimmen
} TGAHeader;
typedef struct
{
GLubyte header[6]; // enthält die ersten 6 brauchbaren Bytes der Datei
GLuint bytesPerPixel; // Anzahl an BYTES Pro Pixel (3 oder 4)
GLuint imageSize; // Menge an benötigtem Speicher, um das Bild zu speichern
GLuint type; // Die Art des Bildes, GL_RGB oder GL_RGBA
GLuint Height; // Höhe des Bildes
GLuint Width; // Breite des Bildes
GLuint Bpp; // Anzahl an BITS Pro Pixel (24 oder 32)
} TGA;
TGAHeader tgaheader; // wird benutzt um unseren Datei-Header zu speichern TGA tga; // wird benutzt um Datei Informationen zu speichern
// unkomprimierter TGA Header
GLubyte uTGAcompare[12] = {0,0, 2,0,0,0,0,0,0,0,0,0};
// kompromierter TGA Header
GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};
// Lade eine unkomprimierte Datei bool LoadUncompressedTGA(Texture *, char *, FILE *); // Lade eine komprimierte Datei bool LoadCompressedTGA(Texture *, char *, FILE *);
#include "tga.h" // Inkludiere den Header den wir gerade gemacht haben
// Lade eine TGA Datei!
bool LoadTGA(Texture * texture, char * filename)
{
FILE * fTGA; // deklariere Dateizeiger fTGA = fopen(filename, "rb"); // öffne Datei zum lesen
if(fTGA == NULL) // wenn hier ein Fehler aufgetreten ist
{
...Error code...
return false; // gebe False zurück
}
// versuche den Datei Header zu lesen
if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0)
{
...Error code here...
return false; // gebe False zurück, wenn das fehl schlug
}
// Wenn der Datei-Header mit dem unkomprimierten Header übereinstimmt
if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)
{
// Lade ein unkomprimiertes TGA
LoadUncompressedTGA(texture, filename, fTGA);
}
// wenn der Datei Header mit dem komprimierten Header übereinstimmt
else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)
{
// Lade ein komprimiertes TGA
LoadCompressedTGA(texture, filename, fTGA);
}
else // wenn keins davon übereinstimmt
{
...Error code here...
return false; // gebe False zurück
}
// Lade ein unkomprimiertes TGA!
bool LoadUncompressedTGA(Texture * texture, char * filename, FILE * fTGA)
{
// versuche die nächsten 6 Bytes zu lesen
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)
{
...Error code here...
return false; // gebe False zurück
}
texture->width = tga.header[1] * 256 + tga.header[0]; // berechne Höhe texture->height = tga.header[3] * 256 + tga.header[2]; // berechne die Breite texture->bpp = tga.header[4]; // Berechne Bits Pro Pixel tga.Width = texture->width; // kopiere Breite in die lokale Struktur tga.Height = texture->height; // kopiere die Höhe in die lokale Struktur tga.Bpp = texture->bpp; // kopiere Bpp in die lokale Struktur
// stelle sicher, dass alle Informationen gültig sind
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))
{
...Error code here...
return false; // gebe False zurück
}
if(texture->bpp == 24) // Ist es ein 24bpp Bild? texture->type = GL_RGB; // Wenn ja, setze GL_RGB else // wenn es nicht 24 ist, muss es 32 sein texture->type = GL_RGBA; // deshalb setze die Art auf GL_RGBA
tga.bytesPerPixel = (tga.Bpp / 8); // berechne die BYTES Pro Pixel // berechne den Speicherplatzbedarf für das Bild tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);
// Aloziiere Speicher
texture->imageData = (GLubyte *)malloc(tga.imageSize);
if(texture->imageData == NULL) // stelle sicher, dass die Alloziierung Ok war
{
...Error code here...
return false; // Wenn nicht, gebe False zurück
}
// versuche alle Bilddaten zu lesen
if(fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize)
{
...Error code here...
return false; // wenn das nicht geht, gebe False zurück
}
// Starte die Schleife
for(GLuint cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel)
{
// erstes Byte XOR drittes Byte XOR erstes Byte XOR drittes Byte
texture->imageData[cswap] ^= texture->imageData[cswap+2] ^=
texture->imageData[cswap] ^= texture->imageData[cswap+2];
}
fclose(fTGA); // schließe die Datei
return true; // kehre erfolgreich zurück
}
bool LoadCompressedTGA(Texture * texture, char * filename, FILE * fTGA)
{
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)
{
...Error code here...
}
texture->width = tga.header[1] * 256 + tga.header[0];
texture->height = tga.header[3] * 256 + tga.header[2];
texture->bpp = tga.header[4];
tga.Width = texture->width;
tga.Height = texture->height;
tga.Bpp = texture->bpp;
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))
{
...Error code here...
} }
if(texture->bpp == 24) // Ist es ein 24bpp Bild?
texture->type = GL_RGB; // wenn ja, setze Type auf GL_RGB
else // wenn es nicht 24 ist, muss es 32 sein
texture->type = GL_RGBA; // deshalb setze Type auf GL_RGBA
tga.bytesPerPixel = (tga.Bpp / 8);
tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);
// Alloziiere Speicher um die Bilddaten zu speichern
texture->imageData = (GLubyte *)malloc(tga.imageSize);
if(texture->imageData == NULL) // wenn kein Speicher reserviert werden kann...
{
...Error code here...
return false; // gebe False zurück
}
GLuint pixelcount = tga.Height * tga.Width; // Anzahl der Pixel in dem Bild GLuint currentpixel = 0; // aktueller Pixel den wir aus den Daten lesen GLuint currentbyte = 0; // aktuelles Byte das wir in Imagedata schreiben // Speicherplatz für 1 Pixel GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel);
do // Starte Schleife
{
GLubyte chunkheader = 0; // Variable um den Wert des The Id Chunk zu speichern
if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) // versuche den ChunkHeader zu lesen
{
...Error code...
return false; // wenn das fehl schlug, gebe false zurück
}
if(chunkheader < 128) // Wenn der Chunk ein 'RAW' Chunk ist
{
chunkheader++; // Addiere 1 zu dem Wert um die absolute Anzahl an Raw Pixel zu erhalten
// Starte Pixel-Lesen-Schleife
for(short counter = 0; counter < chunkheader; counter++)
{
// versuche 1 Pixel zu lesen
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)
{
...Error code...
return false; // wenn es fehl schlug, gebe False zurück
}
texture->imageData[currentbyte] = colorbuffer[2]; // Schreibe das 'R' Byte
texture->imageData[currentbyte + 1 ] = colorbuffer[1]; // Schreibe das 'G' Byte
texture->imageData[currentbyte + 2 ] = colorbuffer[0]; // Schreibe das 'B' Byte
if(tga.bytesPerPixel == 4) // wenn es ein 32bpp Bild ist...
{
texture->imageData[currentbyte + 3] = colorbuffer[3]; // Schreibe das 'A' Byte
}
// Inkrementiere den Byte Zähler um die Anzahl der Bytes in einem Pixel
currentbyte += tga.bytesPerPixel;
currentpixel++; // Inkrementiere die Anzahl der Pixel um 1
else // wenn es ein RLE Header ist
{
chunkheader -= 127; // Subtrahiere 127, um das ID Bit loszuwerden
// lese den nächsten Pixel
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)
{
...Error code...
return false; // wenn es schief geht, gebe False zurück
}
// Starte die Schleife
for(short counter = 0; counter < chunkheader; counter++)
{
// kopiere das 'R' Byte
texture->imageData[currentbyte] = colorbuffer[2];
// kopiere das 'G' Byte
texture->imageData[currentbyte + 1 ] = colorbuffer[1];
// kopiere das 'B' Byte
texture->imageData[currentbyte + 2 ] = colorbuffer[0];
if(tga.bytesPerPixel == 4) // Wenn es ein 32bpp Bild ist
{
// kopiere das 'A' Byte
texture->imageData[currentbyte + 3] = colorbuffer[3];
}
currentbyte += tga.bytesPerPixel; // Inkrementiere den Byte Zähler
currentpixel++; // Inkrementiere den Pixel Zähler
while(currentpixel < pixelcount); // noch mehr Pixel zum lesen?... durchlaufe Schleife erneut fclose(fTGA); // Schließe Datei return true; // kehre erfolgreich zurück }